Skip to content

docs(readme): reframe intro around two-sided autonomy and the accountability layer#9117

Merged
JSONbored merged 4 commits into
mainfrom
claude/loopover-project-pitch-7ab4ff
Jul 27, 2026
Merged

docs(readme): reframe intro around two-sided autonomy and the accountability layer#9117
JSONbored merged 4 commits into
mainfrom
claude/loopover-project-pitch-7ab4ff

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Replaces the stale "deterministic control plane" opening — that described the original project scope. The intro now leads with what LoopOver actually is today: the autonomous maintainer review agent, the miner stack (MCP copilot + autonomous miner runtime), and the outcome-scored accountability layer (fairness report, certified close-precision guarantee, replayable backtest corpus). Also drops "autonomous PR agent" from the it-is-not list, since the miner runtime is exactly that now.

Paired with (already applied directly): updated GitHub repo description and topics.

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored JSONbored self-assigned this Jul 27, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
loopover-ui 85ed584 Commit Preview URL

Branch Preview URL
Jul 27 2026, 01:55 AM

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 467 bytes (0.01%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
loopover-ui 7.42MB 467 bytes (0.01%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: loopover-ui

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/add-scalar-classes-ee0b2Cl_.js (New) 2.17MB 2.17MB 100.0% 🚀
assets/tanstack-vendor-Cg_BYPQK.js (New) 803.11kB 803.11kB 100.0% 🚀
assets/docs.fumadocs-spike-api-reference-BwOPAynK.js (New) 442.88kB 442.88kB 100.0% 🚀
assets/AgentScalarChatInterface.vue-ClUexos_.js (New) 201.71kB 201.71kB 100.0% 🚀
assets/modal-BSxREDWg.js (New) 184.39kB 184.39kB 100.0% 🚀
assets/client-Cs4WBehu.js (New) 146.06kB 146.06kB 100.0% 🚀
assets/maintainer-panel-MMe6FHxX.js (New) 79.0kB 79.0kB 100.0% 🚀
assets/routes-D9v-vIKq.js (New) 35.47kB 35.47kB 100.0% 🚀
assets/owner-panel-BMzRH0MX.js (New) 27.52kB 27.52kB 100.0% 🚀
assets/app-CmFv5_3G.js (New) 25.78kB 25.78kB 100.0% 🚀
assets/ui-vendor-BnXQS48c.js (New) 22.28kB 22.28kB 100.0% 🚀
assets/miner-panel-BA86fmx2.js (New) 20.24kB 20.24kB 100.0% 🚀
assets/app.runs-DGbUGDp1.js (New) 20.22kB 20.22kB 100.0% 🚀
assets/beta-onboarding-DxiAUXfd.js (New) 17.65kB 17.65kB 100.0% 🚀
assets/api._op-CkmvcvUc.js (New) 17.57kB 17.57kB 100.0% 🚀
assets/self-hosting-docs-audit-Bm3kJFww.js (New) 16.6kB 16.6kB 100.0% 🚀
assets/docs._slug-ecNA9DBW.js (New) 15.37kB 15.37kB 100.0% 🚀
assets/playground-panel-CbHj8jSd.js (New) 14.43kB 14.43kB 100.0% 🚀
assets/fairness--y3bMI80.js (New) 10.6kB 10.6kB 100.0% 🚀
assets/app.audit-cn2Kwz99.js (New) 10.08kB 10.08kB 100.0% 🚀
assets/app.config-generator-Ru9vhShH.js (New) 10.06kB 10.06kB 100.0% 🚀
assets/maintainers-WbflouN9.js (New) 8.06kB 8.06kB 100.0% 🚀
assets/miners-D0Jrk-VA.js (New) 7.91kB 7.91kB 100.0% 🚀
assets/agents-CEU5YkH2.js (New) 7.74kB 7.74kB 100.0% 🚀
assets/commands-panel-BhCv0IVF.js (New) 6.65kB 6.65kB 100.0% 🚀
assets/maintainer-workflow-Cihtcg3x.js (New) 6.52kB 6.52kB 100.0% 🚀
assets/digest-panel-BV9-PWth.js (New) 6.15kB 6.15kB 100.0% 🚀
assets/repos._owner._repo.quality-BcncestP.js (New) 6.14kB 6.14kB 100.0% 🚀
assets/docs-nav-DFwVvMm_.js (New) 5.95kB 5.95kB 100.0% 🚀
assets/docs.index-C7zfKy5Y.js (New) 5.95kB 5.95kB 100.0% 🚀
assets/api.index-CG6Nc1ht.js (New) 4.7kB 4.7kB 100.0% 🚀
assets/upstream-drift-DRUJBj-H.js (New) 3.97kB 3.97kB 100.0% 🚀
assets/docs-Bd-0pW9j.js (New) 2.7kB 2.7kB 100.0% 🚀
assets/api-CqprnF2W.js (New) 2.69kB 2.69kB 100.0% 🚀
assets/docs-page-Cq_2ryIE.js (New) 2.1kB 2.1kB 100.0% 🚀
assets/table-DHkrel6E.js (New) 1.75kB 1.75kB 100.0% 🚀
assets/app.workbench-CBQxytIf.js (New) 1.58kB 1.58kB 100.0% 🚀
assets/tabs-cgKOBquV.js (New) 1.39kB 1.39kB 100.0% 🚀
assets/app.repos-zdvlYgQr.js (New) 1.07kB 1.07kB 100.0% 🚀
assets/input-BCGgBqqB.js (New) 796 bytes 796 bytes 100.0% 🚀
assets/file-cog-B1HHhkvU.js (New) 758 bytes 758 bytes 100.0% 🚀
assets/app.maintainer-NAhS9cUt.js (New) 502 bytes 502 bytes 100.0% 🚀
assets/app.owner-QaZ_3IYt.js (New) 474 bytes 474 bytes 100.0% 🚀
assets/app.commands-BNap98Dc.js (New) 455 bytes 455 bytes 100.0% 🚀
assets/app.playground-B5i2w-VA.js (New) 442 bytes 442 bytes 100.0% 🚀
assets/index-0HBIAtmW.js (New) 438 bytes 438 bytes 100.0% 🚀
assets/app.digest-C4RbW2Lv.js (New) 430 bytes 430 bytes 100.0% 🚀
assets/eye-off-X-S_M1zk.js (New) 430 bytes 430 bytes 100.0% 🚀
assets/app.miner-CPiLQIRM.js (New) 422 bytes 422 bytes 100.0% 🚀
assets/key-round-CLKBpbQN.js (New) 355 bytes 355 bytes 100.0% 🚀
assets/bot-Bn14MpJx.js (New) 328 bytes 328 bytes 100.0% 🚀
assets/trash-2-CaVkVPAI.js (New) 328 bytes 328 bytes 100.0% 🚀
assets/save-f9Xbe-Qg.js (New) 327 bytes 327 bytes 100.0% 🚀
assets/git-pull-request-arrow-B5AX2e1j.js (New) 321 bytes 321 bytes 100.0% 🚀
assets/list-checks-DGAiSipb.js (New) 279 bytes 279 bytes 100.0% 🚀
assets/compass-OUvqzWKI.js (New) 251 bytes 251 bytes 100.0% 🚀
assets/history-DNIoTUzc.js (New) 237 bytes 237 bytes 100.0% 🚀
assets/message-square-CJiy-QJH.js (New) 233 bytes 233 bytes 100.0% 🚀
assets/lock-BwglFUrB.js (New) 206 bytes 206 bytes 100.0% 🚀
assets/rotate-cw-Du5Y3hOD.js (New) 201 bytes 201 bytes 100.0% 🚀
assets/play-BPF9oyBW.js (New) 190 bytes 190 bytes 100.0% 🚀
assets/circle-check-B3s9IreM.js (New) 178 bytes 178 bytes 100.0% 🚀
assets/search-B-aUPA6e.js (New) 174 bytes 174 bytes 100.0% 🚀
assets/add-scalar-classes-grsVESf2.js (Deleted) -2.17MB 0 bytes -100.0% 🗑️
assets/tanstack-vendor-CnoIAkSZ.js (Deleted) -802.87kB 0 bytes -100.0% 🗑️
assets/docs.fumadocs-spike-api-reference-CpG7mWiw.js (Deleted) -442.88kB 0 bytes -100.0% 🗑️
assets/AgentScalarChatInterface.vue-BE7M0o83.js (Deleted) -201.71kB 0 bytes -100.0% 🗑️
assets/modal-BU7MF4ac.js (Deleted) -184.39kB 0 bytes -100.0% 🗑️
assets/client-BJwjiLWJ.js (Deleted) -146.06kB 0 bytes -100.0% 🗑️
assets/maintainer-panel-AWaHeS9w.js (Deleted) -79.0kB 0 bytes -100.0% 🗑️
assets/routes-D4o7w2eM.js (Deleted) -35.29kB 0 bytes -100.0% 🗑️
assets/owner-panel-CFucpl6C.js (Deleted) -27.52kB 0 bytes -100.0% 🗑️
assets/app-C4JDLFEW.js (Deleted) -25.78kB 0 bytes -100.0% 🗑️
assets/ui-vendor-D3NLSfKU.js (Deleted) -22.28kB 0 bytes -100.0% 🗑️
assets/miner-panel-D5CnNVW-.js (Deleted) -20.24kB 0 bytes -100.0% 🗑️
assets/app.runs-DkXcjkch.js (Deleted) -20.22kB 0 bytes -100.0% 🗑️
assets/beta-onboarding-BYeuZvHa.js (Deleted) -17.58kB 0 bytes -100.0% 🗑️
assets/api._op-DV9P0be2.js (Deleted) -17.57kB 0 bytes -100.0% 🗑️
assets/self-hosting-docs-audit-BlVjAv2Q.js (Deleted) -16.6kB 0 bytes -100.0% 🗑️
assets/docs._slug-B0Pr40Uv.js (Deleted) -15.37kB 0 bytes -100.0% 🗑️
assets/playground-panel-Bw9drEEX.js (Deleted) -14.43kB 0 bytes -100.0% 🗑️
assets/fairness-yj58RB8R.js (Deleted) -10.6kB 0 bytes -100.0% 🗑️
assets/app.audit-DyP4peXo.js (Deleted) -10.08kB 0 bytes -100.0% 🗑️
assets/app.config-generator-D7VRXmhJ.js (Deleted) -10.06kB 0 bytes -100.0% 🗑️
assets/maintainers-iD0HRQ28.js (Deleted) -8.06kB 0 bytes -100.0% 🗑️
assets/miners-CABClz1P.js (Deleted) -7.91kB 0 bytes -100.0% 🗑️
assets/agents-t8XNFz6p.js (Deleted) -7.74kB 0 bytes -100.0% 🗑️
assets/commands-panel-CiMtoLVY.js (Deleted) -6.65kB 0 bytes -100.0% 🗑️
assets/maintainer-workflow-XxB7ohms.js (Deleted) -6.52kB 0 bytes -100.0% 🗑️
assets/digest-panel-D1gWvgL8.js (Deleted) -6.15kB 0 bytes -100.0% 🗑️
assets/repos._owner._repo.quality-CJ2MTS7l.js (Deleted) -6.14kB 0 bytes -100.0% 🗑️
assets/docs-nav-COrvNwb0.js (Deleted) -5.95kB 0 bytes -100.0% 🗑️
assets/docs.index-6cPcvsDR.js (Deleted) -5.95kB 0 bytes -100.0% 🗑️
assets/api.index-xW-AY3ld.js (Deleted) -4.7kB 0 bytes -100.0% 🗑️
assets/upstream-drift-DO1Hc7Kt.js (Deleted) -3.98kB 0 bytes -100.0% 🗑️
assets/docs-CyEqXJ2N.js (Deleted) -2.7kB 0 bytes -100.0% 🗑️
assets/api-CcWSKyd8.js (Deleted) -2.69kB 0 bytes -100.0% 🗑️
assets/docs-page-BzNyExcl.js (Deleted) -2.1kB 0 bytes -100.0% 🗑️
assets/table-BgJaGIyy.js (Deleted) -1.75kB 0 bytes -100.0% 🗑️
assets/app.workbench-DerPJ6O1.js (Deleted) -1.58kB 0 bytes -100.0% 🗑️
assets/tabs-CYn94ZaL.js (Deleted) -1.39kB 0 bytes -100.0% 🗑️
assets/app.repos-BV5lJDs5.js (Deleted) -1.07kB 0 bytes -100.0% 🗑️
assets/input-DAMGx81x.js (Deleted) -796 bytes 0 bytes -100.0% 🗑️
assets/file-cog-MEme44Yv.js (Deleted) -758 bytes 0 bytes -100.0% 🗑️
assets/app.maintainer-vpYuHXuC.js (Deleted) -502 bytes 0 bytes -100.0% 🗑️
assets/app.owner-Dvzrx47b.js (Deleted) -474 bytes 0 bytes -100.0% 🗑️
assets/app.commands-Ce_BbV7J.js (Deleted) -455 bytes 0 bytes -100.0% 🗑️
assets/app.playground-DBoYTXiK.js (Deleted) -442 bytes 0 bytes -100.0% 🗑️
assets/index-DTXm6rRG.js (Deleted) -438 bytes 0 bytes -100.0% 🗑️
assets/app.digest-D8FUS9lt.js (Deleted) -430 bytes 0 bytes -100.0% 🗑️
assets/eye-off-BiJVi4vO.js (Deleted) -430 bytes 0 bytes -100.0% 🗑️
assets/app.miner-CXTGjJQ_.js (Deleted) -422 bytes 0 bytes -100.0% 🗑️
assets/key-round-CB_X4Qw4.js (Deleted) -355 bytes 0 bytes -100.0% 🗑️
assets/bot-DY3K9wHO.js (Deleted) -328 bytes 0 bytes -100.0% 🗑️
assets/trash-2-CjLn4x-L.js (Deleted) -328 bytes 0 bytes -100.0% 🗑️
assets/save-MyJmc0w5.js (Deleted) -327 bytes 0 bytes -100.0% 🗑️
assets/git-pull-request-arrow-Bv0feZjF.js (Deleted) -321 bytes 0 bytes -100.0% 🗑️
assets/list-checks-BMHhwxdV.js (Deleted) -279 bytes 0 bytes -100.0% 🗑️
assets/compass-BHgjnVL9.js (Deleted) -251 bytes 0 bytes -100.0% 🗑️
assets/history-BV5ah3Ke.js (Deleted) -237 bytes 0 bytes -100.0% 🗑️
assets/message-square-C4LTpg0-.js (Deleted) -233 bytes 0 bytes -100.0% 🗑️
assets/lock-DNJjX-u-.js (Deleted) -206 bytes 0 bytes -100.0% 🗑️
assets/rotate-cw-CYHj_Zpm.js (Deleted) -201 bytes 0 bytes -100.0% 🗑️
assets/play-BFEHPP_C.js (Deleted) -190 bytes 0 bytes -100.0% 🗑️
assets/circle-check-C_cKzPMP.js (Deleted) -178 bytes 0 bytes -100.0% 🗑️
assets/search-C3cizFUk.js (Deleted) -174 bytes 0 bytes -100.0% 🗑️

@JSONbored
JSONbored merged commit 768d776 into main Jul 27, 2026
7 of 8 checks passed
@JSONbored
JSONbored deleted the claude/loopover-project-pitch-7ab4ff branch July 27, 2026 02:04
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
22148 1 22147 21
View the top 1 failed test(s) by shortest run time
test/unit/docs-beta-onboarding.test.ts > docs beta onboarding page > positions LoopOver as independent control-plane, not official Gittensor frontend
Stack Traces | 0.0117s run time
AssertionError: expected '---\ntitle: Beta onboarding by role\n…' to match /base-agent/i

- Expected:
/base-agent/i

+ Received:
"---
title: Beta onboarding by role
description: \"Pick the lane that matches you. Each path ends in a concrete first win — install, configure, or read a report — without treating LoopOver as an official Gittensor product surface.\"
eyebrow: Get started
---

<Callout>
  **Product positioning.** LoopOver is an agent stack for both sides of the pull request on any
  GitHub repo, with Gittensor-native intelligence built in. It is
  [jsonbored/loopover](https://github.com/jsonbored/loopover), independent of
  [entrius/gittensor](https://github.com/entrius/gittensor). Use it to plan work, preflight
  branches, and keep GitHub review surfaces quiet — not as an official Gittensor frontend, wallet
  UI, or payout dashboard.
</Callout>

## Miner journey

Miners and contributors use the local MCP package. Source contents stay on your machine but
branch metadata (such as branch names, SHAs, changed file paths, commit messages, validation
details, labels, body text, linked issues, and scenario notes) is sent to authenticated
LoopOver MCP/API responses for analysis and packet preparation.

**1. Install the MCP.** Global install or `npx` — see [Quickstart](/docs/quickstart).

<CodeBlock
  code={`npm i -g @loopover/mcp@latest
loopover-mcp --help`}
/>

**2. Sign in.** GitHub Device Flow — no PAT storage.

<CodeBlock
  code={`loopover-mcp login
loopover-mcp whoami`}
/>

**3. Run diagnostics.** Confirms API reachability, auth, source-upload posture, and optional
local score-preview wiring.

<CodeBlock code=\"loopover-mcp doctor\" />

**4. Plan next work.** Ranked actions, lane context, and blockers — copilot-only; does not open
PRs or post comments.

<CodeBlock
  code={`loopover-mcp agent plan --login your-login --json
# optional: --repo owner/repo`}
/>

**5. Preflight the branch.** Branch blockers, queue pressure, and maintainer-fit notes before
you push.

<CodeBlock
  code={`loopover-mcp analyze-branch --login your-login --json
loopover-mcp preflight --login your-login --json`}
/>

**6. Prepare a public-safe packet.** Maintainer-readable PR description with no private scoring
language.

<CodeBlock code={`loopover-mcp agent packet --login your-login --repo owner/repo --json`} />

Wire the same tools into Codex, Claude Desktop, or Cursor via
[MCP client setup](/docs/mcp-clients). Signed-in miners can also use the
[Workbench](/app/workbench) and [Miner dashboard](/app/miner) in the control panel.

## Maintainer journey

Maintainers self-host the review stack and install a GitHub App, configure per-repo policy,
preview what could appear on a confirmed-miner PR, then pull context on demand.

**1. Self-host, then install your own App.** Choose repositories and approve permissions —
default posture is silence. Start with
[self-hosting setup](/docs/maintainer-self-hosting), which covers the
direct App's install checklist, then [GitHub App configuration](/docs/github-app) for the
review behavior (PR panel, checks, gate modes).

**2. Configure settings.** Opt in to at most one configured label and one sticky sanitized
comment per confirmed-miner PR. Tune repo policy in installation settings or via the API.

**3. Preview the public surface.** Dry-run what would be written to GitHub without mutating
state. Keep **LoopOver Context** advisory; require **LoopOver Orb Review Agent** only after
blocking rules are explicitly configured.

<CodeBlock
  lang=\"http\"
  code={`POST /v1/repos/:owner/:repo/settings-preview
# body: sample PR fields + desired policy flags`}
/>

The signed-in [Maintainer console](/app/maintainer) and [Repos](/app/repos) tab surface the
same preview diff when live data is available.

**4. Use maintainer commands.** On-demand context in the PR thread — output stays
maintainer-scoped when appropriate.

<CodeBlock
  code={`@loopover help
@loopover preflight
@loopover blockers
@loopover duplicate-check
@loopover miner-context
@loopover next-action
@loopover reviewability`}
/>

Deeper workflow: [Maintainer workflow](/docs/maintainer-workflow). Privacy rules:
[Privacy & security](/docs/privacy-security).

## Repo owner journey

Repo owners care about registration readiness and sensible `.loopover.yml` configuration before
promoting labels or maintainer-cut policy.

**1. Run a readiness report.** Blockers, warnings, recommended registration mode, and issue
policy — private API only.

<CodeBlock lang=\"http\" code={`GET /v1/repos/:owner/:repo/registration-readiness`} />

**2. Review config guidance.** Recommended config diff with reasons and tradeoffs — apply via PR
when ready.

<CodeBlock lang=\"http\" code={`GET /v1/repos/:owner/:repo/gittensor-config-recommendation`} />

**3. Use the control panel.** Open [Repository owner](/app/owner) (or the Owner tab under
[Repos](/app/repos)) to inspect the same signals with a live repo selector after you sign in
with GitHub.

Readiness is separate from upstream drift: a repo can look ready while Gittensor rules are stale.
Check [Upstream drift](/docs/upstream-drift) when you change scoring assumptions.

## Operator journey

Operators watch deployment health, product usage, value rollups, and upstream drift across
installations. These surfaces are private and authenticated — never mirrored to public GitHub
comments.

**1. Open usage & value.** Weekly rollups, activation status, and noise-reduction metrics in the
control panel.

[Operator dashboard](/app/operator) — backed by `GET .../v1/app/operator-dashboard`.

**2. Read the weekly value report.** Summary lines plus rollup freshness and warnings when
backfills lag or fidelity degrades.

**3. Check drift status.** Compare ruleset snapshots and signal fidelity before trusting miner or
maintainer guidance.

<CodeBlock
  lang=\"http\"
  code={`GET .../v1/upstream/drift
GET .../v1/upstream/status
GET /v1/readiness`}
/>

When drift is not `current`, treat MCP and API responses as tied to the printed ruleset version.
See [Upstream drift](/docs/upstream-drift) for semantics.

## Public vs private boundaries

Public GitHub output must never include wallets, hotkeys, payout or reward estimates, raw trust
scores, public score estimates, private reviewability details, or farming language. Private MCP,
API, and control-panel surfaces may show authenticated scoreability, blockers, projections, and
evidence — framed as guidance, not guaranteed outcomes.

<Callout variant=\"safety\">
  If you are unsure whether copy belongs on a PR thread, start with a maintainer packet or MCP
  preflight. Public comments stay advisory and sanitized; richer context stays in private channels.
</Callout>

## Next steps

- Miners: [Quickstart](/docs/quickstart) → [Miner workflow](/docs/miner-workflow)
- Maintainers: [Self-hosting](/docs/maintainer-self-hosting) →
  [GitHub App](/docs/github-app) → [Maintainer workflow](/docs/maintainer-workflow)
- Repo owners: [Owner console](/app/owner) + [Privacy & security](/docs/privacy-security)
- Operators: [Operator dashboard](/app/operator) + [Upstream drift](/docs/upstream-drift)
"

 ❯ test/unit/docs-beta-onboarding.test.ts:54:20

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

JSONbored added a commit that referenced this pull request Jul 27, 2026
…repair scans for state a crash used to lose

Four ways a badly-timed process death left permanent damage. #9007 made this
urgent rather than theoretical: busy deploys currently end in SIGKILL, so "killed
at the wrong moment" is the normal case, not the rare one.

On Postgres the queue is deliberately multi-instance (that is what the FOR UPDATE
SKIP LOCKED claim is for), so "every processing row" is not "my crashed
predecessor's work" — during any overlapped or rolling deploy it also includes
jobs a SIBLING is running right now. Both processes then ran the same PR pass
concurrently, producing duplicate gate check-runs and verdict thrash. Recovery is
now scoped to rows whose lease has actually expired, the same definition the
runtime reaper already uses, so boot and steady-state stop disagreeing about what
"abandoned" means, and the re-pend is a compare-and-swap so a sibling finishing
first cannot be resurrected.

Two related holes in the same file. The lease is now heartbeated by the worker
that holds it: without that, the timeout was a ceiling on job DURATION rather than
on liveness, so a legitimately long pass — a large PR, a slow AI review, a
rate-limited GitHub window — was reclaimed and double-run purely for taking too
long, and the activeJobIds guard meant to prevent that only covers one process's
in-memory set, which is the wrong scope for a shared queue. And a reclaim now
costs an attempt: it never did, so a job that reliably wedges past its lease was
requeued forever with attempts frozen, never reaching the dead-letter threshold
that exists precisely to stop a poison job from burning the queue.

Statements ran individually and the file was recorded applied only after the last
one succeeded, so a crash mid-file re-ran the WHOLE file next boot. Harmless for
pure DDL and destructive for the several migrations carrying real DML — the
table-rebuild INSERT ... SELECT pattern, plus UPDATE/DELETE steps — where a re-run
either double-inserts or raises a PK conflict that, not matching the tolerated
"already exists" shape, throws and bricks boot outright. Each file now runs as one
transaction with its ledger row committed inside it, via a new execTransaction on
both self-host adapters. It could not reuse exec: on Postgres each exec call takes
whatever connection the pool hands out, so a BEGIN and its COMMIT issued as
separate calls can land on different connections and mean nothing.

The pre-existing drift tolerance is preserved rather than replaced — a database
that already drifted must still heal itself instead of failing to boot, and that
only works per-statement. But ONLY drift falls back. Any other failure rethrows
with the transaction already rolled back, because retrying a genuinely broken file
per-statement would re-apply its valid statements after the rollback and leave
exactly the partial state this fix exists to prevent.

pr_outcome is what the fleet calibration export INNER JOINs gate_decision against,
so a PR missing one does not lose a data point, it vanishes from calibration
entirely — neither numerator nor denominator. Both writers are in-process and
best-effort: a process that dies between the GitHub mutation and the record call
loses the direct write, and on the next pass the PR is already terminal so the
planner plans nothing and it never fires; the webhook that would have caught it
was delivered while the container was down and GitHub does not redeliver. Nothing
scanned for the gap — the repair sweep only visits OPEN PRs. These losses are not
a random sample: a superseded close is by definition a wrong close, so they skew
toward the gate's mistakes and their absence biased published accuracy upward.

Pass 1 of the flag-then-close double-check enqueues a single delayed job to run
Pass 2, and that message was the only thing that could finish the sequence. Its
documented "next sweep / CI event" backstop is vacuous: #never-endless-reregate
makes an already-regated PR permanently sweep-ineligible while the flag itself
suppresses merge and approve, and the violation memory is permanent so the flag
could never clear either. Pass 1 now also records the deadline, and a watchdog
re-enqueues Pass 2 for any flag past it. audit_events is the right store: durable,
already queried by type, and repo-agnostic — unlike the label, which is per-repo
configurable and would need settings resolution before a cross-repo scan could
even recognize it.

Both repair scans ride the re-gate sweep's existing fan-out tick rather than each
earning a job type and a cron entry for a bounded DB scan, and run before the
fan-out so neither can cost the tick its actual work.

Also fixes main: test/unit/docs-beta-onboarding.test.ts asserted the doc still
says "base-agent", which #9117 intentionally retired when it repositioned the
product away from "a deterministic base-agent for the Gittensor ecosystem" toward
an agent stack for both sides of the pull request on any GitHub repo. The guard
exists to stop LoopOver claiming to BE the official Gittensor frontend, and its
other four assertions cover that on their own; pinning the retired wording only
made it veto an intended product change.

Local gate green end to end (npm run test:ci, exit 0). 100% line and branch
coverage on all 210 added src lines.

Closes #9023
Closes #9026
Closes #9027
Closes #9031
JSONbored added a commit that referenced this pull request Jul 27, 2026
…repair scans for state a crash used to lose (#9144)

Four ways a badly-timed process death left permanent damage. #9007 made this
urgent rather than theoretical: busy deploys currently end in SIGKILL, so "killed
at the wrong moment" is the normal case, not the rare one.

On Postgres the queue is deliberately multi-instance (that is what the FOR UPDATE
SKIP LOCKED claim is for), so "every processing row" is not "my crashed
predecessor's work" — during any overlapped or rolling deploy it also includes
jobs a SIBLING is running right now. Both processes then ran the same PR pass
concurrently, producing duplicate gate check-runs and verdict thrash. Recovery is
now scoped to rows whose lease has actually expired, the same definition the
runtime reaper already uses, so boot and steady-state stop disagreeing about what
"abandoned" means, and the re-pend is a compare-and-swap so a sibling finishing
first cannot be resurrected.

Two related holes in the same file. The lease is now heartbeated by the worker
that holds it: without that, the timeout was a ceiling on job DURATION rather than
on liveness, so a legitimately long pass — a large PR, a slow AI review, a
rate-limited GitHub window — was reclaimed and double-run purely for taking too
long, and the activeJobIds guard meant to prevent that only covers one process's
in-memory set, which is the wrong scope for a shared queue. And a reclaim now
costs an attempt: it never did, so a job that reliably wedges past its lease was
requeued forever with attempts frozen, never reaching the dead-letter threshold
that exists precisely to stop a poison job from burning the queue.

Statements ran individually and the file was recorded applied only after the last
one succeeded, so a crash mid-file re-ran the WHOLE file next boot. Harmless for
pure DDL and destructive for the several migrations carrying real DML — the
table-rebuild INSERT ... SELECT pattern, plus UPDATE/DELETE steps — where a re-run
either double-inserts or raises a PK conflict that, not matching the tolerated
"already exists" shape, throws and bricks boot outright. Each file now runs as one
transaction with its ledger row committed inside it, via a new execTransaction on
both self-host adapters. It could not reuse exec: on Postgres each exec call takes
whatever connection the pool hands out, so a BEGIN and its COMMIT issued as
separate calls can land on different connections and mean nothing.

The pre-existing drift tolerance is preserved rather than replaced — a database
that already drifted must still heal itself instead of failing to boot, and that
only works per-statement. But ONLY drift falls back. Any other failure rethrows
with the transaction already rolled back, because retrying a genuinely broken file
per-statement would re-apply its valid statements after the rollback and leave
exactly the partial state this fix exists to prevent.

pr_outcome is what the fleet calibration export INNER JOINs gate_decision against,
so a PR missing one does not lose a data point, it vanishes from calibration
entirely — neither numerator nor denominator. Both writers are in-process and
best-effort: a process that dies between the GitHub mutation and the record call
loses the direct write, and on the next pass the PR is already terminal so the
planner plans nothing and it never fires; the webhook that would have caught it
was delivered while the container was down and GitHub does not redeliver. Nothing
scanned for the gap — the repair sweep only visits OPEN PRs. These losses are not
a random sample: a superseded close is by definition a wrong close, so they skew
toward the gate's mistakes and their absence biased published accuracy upward.

Pass 1 of the flag-then-close double-check enqueues a single delayed job to run
Pass 2, and that message was the only thing that could finish the sequence. Its
documented "next sweep / CI event" backstop is vacuous: #never-endless-reregate
makes an already-regated PR permanently sweep-ineligible while the flag itself
suppresses merge and approve, and the violation memory is permanent so the flag
could never clear either. Pass 1 now also records the deadline, and a watchdog
re-enqueues Pass 2 for any flag past it. audit_events is the right store: durable,
already queried by type, and repo-agnostic — unlike the label, which is per-repo
configurable and would need settings resolution before a cross-repo scan could
even recognize it.

Both repair scans ride the re-gate sweep's existing fan-out tick rather than each
earning a job type and a cron entry for a bounded DB scan, and run before the
fan-out so neither can cost the tick its actual work.

Also fixes main: test/unit/docs-beta-onboarding.test.ts asserted the doc still
says "base-agent", which #9117 intentionally retired when it repositioned the
product away from "a deterministic base-agent for the Gittensor ecosystem" toward
an agent stack for both sides of the pull request on any GitHub repo. The guard
exists to stop LoopOver claiming to BE the official Gittensor frontend, and its
other four assertions cover that on their own; pinning the retired wording only
made it veto an intended product change.

Local gate green end to end (npm run test:ci, exit 0). 100% line and branch
coverage on all 210 added src lines.

Closes #9023
Closes #9026
Closes #9027
Closes #9031
JSONbored added a commit that referenced this pull request Jul 27, 2026
…ors, and stop judging a fix you cannot see (#9145)

Three ways the AI reviewer acted confidently on input it had silently altered,
truncated, or should never have trusted in the first place.

#9035 — attacker-controlled text reached the prompt with no structural defense.
Title, body and diff were concatenated straight in. "Judge ONLY the diff" tells
the model what to look at; it never says that what it is looking at is DATA. The
only defense was regex defang, which is deliberately narrow and which paraphrase
or encoding walks past — and the same text reaches BOTH consensus reviewers, so a
successful steer suppresses both and never even trips the single-rejection
ai_review_split rule that exists to catch one reviewer being wrong.

Each untrusted region is now fenced, with a system rule saying content between the
markers is data and cannot change the rules, the output format, or the verdict.
Forged markers are stripped from inside the region so a body cannot close its own
fence early — without that, fencing would be worse than none.

And a DETECTED attempt now holds the PR for a human. Defang recorded these and, by
design, never let them affect the disposition, so a caught attacker got a normal
roll. Manipulation aimed at the reviewer is evidence of intent, and the one thing
it must not buy is an automated decision. Held, never closed: the detector is a
regex running over a repository whose own subject matter is AI review, so a false
positive is entirely possible, and a hold costs a contributor a wait where a close
would cost them their PR. It reuses the existing ai_review_inconclusive hold path,
so no gate-decision twin changes.

#9076 — the defang shifted every inline anchor after it.
The injection patterns' `[^.]{0,N}` gaps deliberately span newlines, so one match
can swallow two or three diff lines including their `+`/`-` markers and even an
`@@` header. Replacing all of that with a single-line literal collapsed those
newlines. The reviewer counts a finding's line over the DEFANGED text, but the
finding is validated and posted against the ORIGINAL patch — so every anchor after
a multi-line redaction shifted, and a shifted anchor that still landed inside the
commentable set passed validation and posted publicly on the wrong line of a
contributor's PR. The redaction now re-emits one newline per newline consumed, so
the two coordinate systems stay congruent.

Two more on the same surface. Blocker-severity findings now anchor to ADDED lines
only: rightSideLinesFromPatch admits unchanged context too, while the prompt asks
for an added line and warns that a wrong line is worse than none — set membership
was the only check, and context satisfies it, so a model miscounting by one to
three lines landed on context and posted. That file conflated two different
properties throughout: "GitHub will accept this anchor" and "this anchor is
correct". Nits still allow context, because a misplaced nit is noise while a
misplaced blocker costs someone their PR. And an empty patch line now counts as
the context line it is instead of desynchronizing every later line number in the
file (the trailing split artifact of a patch ending in a newline is dropped first,
so the two cases stay distinct).

#9075 — a confident "you didn't fix the issue" computed from a window that never
contained the fix.
linked-issue-satisfaction's own header claims "NO gate wiring, NO disposition
change… advisory-only either way". That is stale: under
linkedIssueSatisfactionGateMode: "block" an `unaddressed` verdict pushes a
critical-path finding reading "this PR does not appear to satisfy its linked
issue's scope." Meanwhile the module re-slices the already-truncated diff to 60k
under the header "Unified diff (truncated if large):" — a hedge, not a fact, which
leaves the model to guess whether it is seeing everything. A PR whose fix sits in a
dropped hunk, a filtered file, or past character 60,000 got that public verdict
anyway. The confidence floor does not help: it guards against a model being
unsure, not against a model being sure about the wrong input.

The header now states the fact, and an `unaddressed` verdict over a known-truncated
diff degrades to `partial`. Only `unaddressed` — "addressed" over a truncated diff
is the model finding POSITIVE evidence, which truncation cannot manufacture. Same
reasoning #8961 already applies to truncated PR bodies.

POLICY REVERSAL, called out explicitly. selectContextSectionsWithinBudget's
`break` was pinned by a test asserting it is "a hard priority cutoff, not a
bin-packing optimization". That reasoning holds for sections that are genuinely
model context. It does not hold for what actually sat at the bottom of the list:
testEvidence is ~200 characters and is not context at all but a deterministic
classifier FACT ("this PR changes no test paths"), so one large RAG block silently
discarded it on exactly the large PRs where it matters most, with no marker
anywhere. An oversized section is now skipped rather than ending the loop. Priority
order still decides who gets first refusal on the budget; every included section
still genuinely fits. Both tests encoding the old rule are inverted with the
reasoning in place.

Also fixes main, same one-line change as #9144 (identical, so the two merge
cleanly): docs-beta-onboarding asserted the doc still says "base-agent", which
#9117 intentionally retired when it repositioned the product.

Local gate green end to end (npm run test:ci, exit 0). 100% line and branch
coverage on all 317 added src lines.

Closes #9035
Closes #9075
Closes #9076
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant